package ro.cityprojects.adminloc.dao;

import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.hibernate3.HibernateQueryException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import ro.cityprojects.adminloc.entities.BaseEntity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Repository
public class BasicRepository<PK extends Serializable, Entity extends BaseEntity> {

    protected HibernateTemplate hibernateTemplate;

    @Autowired
    public void setAdminLocSessionFactory(@Qualifier("adminLocSessionFactory") SessionFactory adminLocSessionFactory) {
        hibernateTemplate = new HibernateTemplate(adminLocSessionFactory);
    }

    // Create operations
    public void save(Entity entity) {
        hibernateTemplate.save(entity);
        hibernateTemplate.flush();
    }

    // Read operations
    public Entity get(Class<Entity> entityClass, PK id) {
        Entity entity = hibernateTemplate.get(entityClass, id);

        Assert.notNull(entity, "There is no " + entityClass.getSimpleName().toLowerCase() + " with the primary key " + id);

        return entity;
    }

    @SuppressWarnings("unchecked")
    public Entity getByProperty(Class<Entity> entityClass, String filterName, String filterValue) {
        DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);

        List<Entity> retrievedEntities;
        try {
            Integer value;
            try {
                value = Integer.parseInt(filterValue);
                criteria.add(Restrictions.eq(filterName, value));
            } catch (NumberFormatException x) {
                criteria.add(Restrictions.eq(filterName, filterValue));
            }

            retrievedEntities = hibernateTemplate.findByCriteria(criteria);
        } catch (HibernateQueryException hqe) {
            throw new IllegalArgumentException("Cannot filter '" + entityClass.getSimpleName().toLowerCase() + "' by '" + filterName + "'");
        }

        if (CollectionUtils.isEmpty(retrievedEntities)) {
            return null;
        }

        return retrievedEntities.get(0);
    }

    @SuppressWarnings("unchecked")
    public Set<Entity> getAll(Class<Entity> entityClass, String filterName, String filterValue) {
        List<Entity> retrievedEntities;
        final String className = entityClass.getSimpleName();

        if (StringUtils.isEmpty(filterName)) {
            retrievedEntities = hibernateTemplate.loadAll(entityClass);

            Assert.notEmpty(retrievedEntities, "There are no " + className + "s");
        } else {
            DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);

            try {
                Integer value;
                try {
                    value = Integer.parseInt(filterValue);
                    criteria.add(Restrictions.eq(filterName, value));
                } catch (NumberFormatException x) {
                    criteria.add(Restrictions.eq(filterName, filterValue));
                }

                retrievedEntities = hibernateTemplate.findByCriteria(criteria);
            } catch (HibernateQueryException hqe) {
                throw new IllegalArgumentException("Cannot filter " + className.toLowerCase() + "s by '" + filterName + "'");
            }

            Assert.notEmpty(retrievedEntities, "There are no " + className.toLowerCase() + "s with the '" + filterName + "' '" + filterValue + "'");
        }

        Set<Entity> entities = new HashSet<Entity>();
        for (Entity retrievedEntity : retrievedEntities) {
            entities.add(retrievedEntity);
        }

        return entities;
    }

    // Update operations
    public void update(Entity entity) {
        hibernateTemplate.merge(entity);
    }

    // Delete operations
    public void delete(Class<Entity> entityClass, PK id) {
        Entity entity = hibernateTemplate.get(entityClass, id);
        hibernateTemplate.delete(entity);
        hibernateTemplate.flush();
    }
}
